package senzuri.acceleraudio.util;

import java.util.ArrayList;

public class InfiniteBuffer {

  public static class Short {
    private int count;
    private int chunkSize;
    private ArrayList<short[]> chunks;
    
    public Short(int chunkSize) {
      chunks = new ArrayList<short[]>();
      this.chunkSize = chunkSize;
      count = 0;
    }
    
    public int size() {
      return count;
    }
    
    public void ensureCapacity(int index) {
      while (index / chunkSize >= chunks.size())
        chunks.add(new short[chunkSize]);
    }
    
    public void add(short value) {
      ensureCapacity(count);
      chunks.get(count / chunkSize)[count % chunkSize] = value;
      count++;
    }
    
    public void add(short[] values) {
      add(values, 0, values.length);
    }
    
    public void add(short[] values, int from, int length) {
      ensureCapacity(count + length - 1);
      int copied = 0;
      
      while (copied < length) {
        int currentChunkIndex = count % chunkSize;
        short[] currentBuffer = chunks.get(count / chunkSize);
        int toCopy = Math.min(length - copied, chunkSize - currentChunkIndex);
        System.arraycopy(values, from + copied, currentBuffer, currentChunkIndex, toCopy);
        count += toCopy;
        copied += toCopy;
      }
    }
    
    public short get(int index) {
      return chunks.get(index / chunkSize)[index % chunkSize];
    }
    
    public void put(int index, short value) {
      chunks.get(index / chunkSize)[index % chunkSize] = value;
    }
    
    public short[] getChunk(int index) {
      return chunks.get(index);
    }
    
    public int getChunkSize(int index) {
      return index == count / chunkSize ? count % chunkSize : chunkSize;
    }
    
    public int getChunksCount() {
      return chunks.size();
    }
  }

  public static class Integer {
    private int count;
    private int chunkSize;
    private ArrayList<int[]> chunks;
    
    public Integer(int chunkSize) {
      chunks = new ArrayList<int[]>();
      this.chunkSize = chunkSize;
      count = 0;
    }
    
    public int size() {
      return count;
    }
    
    public void ensureCapacity(int index) {
      while (index / chunkSize >= chunks.size())
        chunks.add(new int[chunkSize]);
    }
    
    public void add(int value) {
      ensureCapacity(count);
      chunks.get(count / chunkSize)[count % chunkSize] = value;
      count++;
    }
    
    public void add(int[] values) {
      add(values, 0, values.length);
    }
    
    public void add(int[] values, int from, int length) {
      ensureCapacity(count + length - 1);
      int copied = 0;
      
      while (copied < length) {
        int currentChunkIndex = count % chunkSize;
        int[] currentBuffer = chunks.get(count / chunkSize);
        int toCopy = Math.min(length - copied, chunkSize - currentChunkIndex);
        System.arraycopy(values, from + copied, currentBuffer, currentChunkIndex, toCopy);
        count += toCopy;
        copied += toCopy;
      }
    }
    
    public int get(int index) {
      return chunks.get(index / chunkSize)[index % chunkSize];
    }
  }

  public static class Byte {
    private int count;
    private int chunkSize;
    private ArrayList<byte[]> chunks;
    
    public Byte(int chunkSize) {
      chunks = new ArrayList<byte[]>();
      this.chunkSize = chunkSize;
      count = 0;
    }
    
    public int size() {
      return count;
    }
    
    public void ensureCapacity(int index) {
      while (index / chunkSize >= chunks.size())
        chunks.add(new byte[chunkSize]);
    }
    
    public void add(byte value) {
      ensureCapacity(count);
      chunks.get(count / chunkSize)[count % chunkSize] = value;
      count++;
    }
    
    public void add(byte[] values) {
      add(values, 0, values.length);
    }
    
    public void add(byte[] values, int from, int length) {
      ensureCapacity(count + length - 1);
      int copied = 0;
      
      while (copied < length) {
        int currentChunkIndex = count % chunkSize;
        byte[] currentBuffer = chunks.get(count / chunkSize);
        int toCopy = Math.min(length - copied, chunkSize - currentChunkIndex);
        System.arraycopy(values, from + copied, currentBuffer, currentChunkIndex, toCopy);
        count += toCopy;
        copied += toCopy;
      }
    }
    
    public byte get(int index) {
      return chunks.get(index / chunkSize)[index % chunkSize];
    }
  }

  public static class Long {
    private int count;
    private int chunkSize;
    private ArrayList<long[]> chunks;
    
    public Long(int chunkSize) {
      chunks = new ArrayList<long[]>();
      this.chunkSize = chunkSize;
      count = 0;
    }
    
    public int size() {
      return count;
    }
    
    public void ensureCapacity(int index) {
      while (index / chunkSize >= chunks.size())
        chunks.add(new long[chunkSize]);
    }
    
    public void add(long value) {
      ensureCapacity(count);
      chunks.get(count / chunkSize)[count % chunkSize] = value;
      count++;
    }
    
    public void add(long[] values) {
      add(values, 0, values.length);
    }
    
    public void add(long[] values, int from, int length) {
      ensureCapacity(count + length - 1);
      int copied = 0;
      
      while (copied < length) {
        int currentChunkIndex = count % chunkSize;
        long[] currentBuffer = chunks.get(count / chunkSize);
        int toCopy = Math.min(length - copied, chunkSize - currentChunkIndex);
        System.arraycopy(values, from + copied, currentBuffer, currentChunkIndex, toCopy);
        count += toCopy;
        copied += toCopy;
      }
    }
    
    public long get(int index) {
      return chunks.get(index / chunkSize)[index % chunkSize];
    }
  }

  public static class Float {
    private int count;
    private int chunkSize;
    private ArrayList<float[]> chunks;
    
    public Float(int chunkSize) {
      chunks = new ArrayList<float[]>();
      this.chunkSize = chunkSize;
      count = 0;
    }
    
    public int size() {
      return count;
    }
    
    public void ensureCapacity(int index) {
      while (index / chunkSize >= chunks.size())
        chunks.add(new float[chunkSize]);
    }
    
    public void add(float value) {
      ensureCapacity(count);
      chunks.get(count / chunkSize)[count % chunkSize] = value;
      count++;
    }
    
    public void add(float[] values) {
      add(values, 0, values.length);
    }
    
    public void add(float[] values, int from, int length) {
      ensureCapacity(count + length - 1);
      int copied = 0;
      
      while (copied < length) {
        int currentChunkIndex = count % chunkSize;
        float[] currentBuffer = chunks.get(count / chunkSize);
        int toCopy = Math.min(length - copied, chunkSize - currentChunkIndex);
        System.arraycopy(values, from + copied, currentBuffer, currentChunkIndex, toCopy);
        count += toCopy;
        copied += toCopy;
      }
    }
    
    public float get(int index) {
      return chunks.get(index / chunkSize)[index % chunkSize];
    }
  }

  public static class Double {
    private int count;
    private int chunkSize;
    private ArrayList<double[]> chunks;
    
    public Double(int chunkSize) {
      chunks = new ArrayList<double[]>();
      this.chunkSize = chunkSize;
      count = 0;
    }
    
    public int size() {
      return count;
    }
    
    public void ensureCapacity(int index) {
      while (index / chunkSize >= chunks.size())
        chunks.add(new double[chunkSize]);
    }
    
    public void add(double value) {
      ensureCapacity(count);
      chunks.get(count / chunkSize)[count % chunkSize] = value;
      count++;
    }
    
    public void add(double[] values) {
      add(values, 0, values.length);
    }
    
    public void add(double[] values, int from, int length) {
      ensureCapacity(count + length - 1);
      int copied = 0;
      
      while (copied < length) {
        int currentChunkIndex = count % chunkSize;
        double[] currentBuffer = chunks.get(count / chunkSize);
        int toCopy = Math.min(length - copied, chunkSize - currentChunkIndex);
        System.arraycopy(values, from + copied, currentBuffer, currentChunkIndex, toCopy);
        count += toCopy;
        copied += toCopy;
      }
    }
    
    public double get(int index) {
      return chunks.get(index / chunkSize)[index % chunkSize];
    }
  }

}
